<?php 
defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * 用户模型
 * 
 * @desc    负责用户相关逻辑
 * @version 1.0.0 
 * @author  Jason
 * @date    2017年4月25日 下午3:20:11
 */
class Mod_member extends MY_Model {
	
	protected $table_mapper = [
			'master' => 'member',
			'info'   => 'member_info',
	];
	
	protected $allowed_fields = [
			'master' => ['role_id', 'name', 'email', 'mobile'],
			'info' => ['nickname', 'avatar', 'cover', 'preferences'],
	];
	
	use models\common\BinaryBitBasedStatus;
	
// 	<h4>A PHP Error was encountered</h4>
// 	<p>Severity: Runtime Notice</p>
// 	<p>Message:  Mod_member and models\common\BinaryBitBasedStatus define the same property ($status_bit_length) in the composition of Mod_member. This might be incompatible, to improve maintainability consider using accessor methods in traits instead. Class was composed</p>
// 	<p>Filename: models/Mod_member.php</p>
// 	<p>Line Number: 141</p>
// 	protected $status_bit_length = 4;
// 	protected $status_bit_positive_tags = ['unlimited', 'checked', 'activated', 'enabled'];
// 	protected $status_bit_negative_tags = ['limited', 'unchecked', 'unactivated', 'disabled'];
	
	public function __construct()
	{
		parent::__construct();
		
		$this->load->database();
		
		$this->status_bit_config = [
				'master' => [
						'status_bit_length' => 4,
						'status_bit_positive_tags' => ['unlimited', 'checked', 'activated', 'enabled'],
						'status_bit_negative_tags' => ['limited', 'unchecked', 'unactivated', 'disabled'],
				],
		];
	}
	
	public function get_member_by_login($name, $password)
	{
		$member = $this->db
						->where('name', $name)
						->or_where('email', $name)
						->get($this->table_mapper['master'])
						->row_array();
		if (empty($member['password']) || 
				!self::calculate_password_verify($password, $member['password'])) {
			$member = [];
		}
		
		return $member;
	}
	
	public function get_member_by_name($name)
	{
		$member = $this->db
						->from($this->table_mapper['master'])
						->where('name', $name)
						->limit(1)
						->get()
						->row_array();
		$member = empty($member) ? [] : $member;
		return $member;
	}
	
	public function get_member_by_email($email)
	{
		$member = $this->db
						->from($this->table_mapper['master'])
						->where('email', $email)
						->limit(1)
						->get()
						->row_array();
		$member = empty($member) ? [] : $member;
		return $member;
	}
	
	public function get_member_by_id($id)
	{
		$member = $this->db
						->WHERE('id', $id)
						->get($this->table_mapper['master'])
						->row_array();
		
		return $member;
	}
	
	public function get_info_by_id($id)
	{
		$member = $this->db
						->WHERE('id', $id)
						->get($this->table_mapper['info'])
						->row_array();
		
		return $member;
	}
	
	/**
	 * 注册一个用户， 走默认的注册信息：角色、状态
	 * @param unknown $post
	 */
	public function register_member($post)
	{
		$this->config->load('dbconst');
		$data = $this->get_allowed_write_data($post, 'master');
		$data['role_id'] = $this->config->item('default_register', 'role_id');
		$data['password'] = self::calculate_password_hash($post['password']);
		$data['create_time'] = time();
		$data['status'] = $this->calculate_status_by_tags(['enabled']);
		$this->db->insert($this->table_mapper['master'], $data);
		$affected_rows = $this->db->affected_rows();
		if ($affected_rows > 0) {
			$member_id = $this->db->insert_id();
			$info_data = [];
			$info_data['id'] = $member_id;
			$this->db->insert($this->table_mapper['info'], $info_data);
		} else {
			$member_id = null;
		}
		$data['id'] = $member_id;
		
		return $data;
	}
	
	public function create_member_with_info($post)
	{
		list($status, $content) = $this->create_member($post);
		if ($status) {
			$post = array_merge($post, $content);
			$member_id = $content['id'];
			list($status, $content) = $this->create_member_info($post, $member_id);
			$content = array_merge($post, $content);
		}
		return [$status, $content];
	}
	public function create_member($post)
	{
		// 1. 整理数据
		// 注意 ?: 的结合性是从左往右（与C、Java等都不一样，从右往左），不清晰、不确定就加上括号吧！
		//$data['status_tags'] = empty($data['status_tags']) ? [] : (is_array($data['status_tags']) ? $data['status_tags'] : explode(',', $data['status_tags']));
		if (empty($post['status_tags'])) {
			$post['status_tags'] = [];
		} else if (!is_array($post['status_tags'])) {
			$post['status_tags'] = explode(',', $post['status_tags']);
		}
		if (!empty($post['use_default_password']) || empty($post['password'])) {
			// TODO config default password
			$default_password = 'password';
			$post['password'] = $default_password;
		}
		$post['password'] = self::calculate_password_hash($post['password']);
		$post['create_time'] = time();
		$post['status'] = $this->calculate_status_by_tags($post['status_tags']);
		
		// 2. 过滤数据
		$fields = $this->db->list_fields($this->table_mapper['master']);
		$data = $this->get_allowed_write_data($post, null, $fields);
		
		// 3. 执行插入
		$this->db->insert($this->table_mapper['master'], $data);
		
		// 4. 整理返回
		$data['id'] = empty($data['id']) ? $this->db->insert_id() : $data['id'];
		$status = $this->db->affected_rows() > 0;
		$content = $data;
		
		return [$status, $content];
	}
	
	public function create_member_info($post, $member_id)
	{
		$fields = $this->db->list_fields($this->table_mapper['info']);
		$info_data = $this->get_allowed_write_data($post, null, $fields);
		
		$this->db->insert($this->table_mapper['info'], $info_data);
		
		$status = $this->db->affected_rows() > 0;
		$content = $info_data;
		
		return [$status, $content];
	}
	
	public function update_member_with_info($post)
	{
		// 1. 整理数据
		if (isset($post['status_tags'])) {
			if (empty($post['status_tags'])) {
				$post['status_tags'] = [];
			} else if (!is_array($post['status_tags'])) {
				$post['status_tags'] = explode(',', $post['status_tags']);
			}
			$post['status'] = $this->calculate_status_by_tags($post['status_tags']);
		}
		
		if (!empty($post['use_default_password'])) {
			// TODO config default password
			$default_password = 'password';
			$post['password'] = $default_password;
		}
		if (isset($post['password'])) {
			$post['password'] = self::calculate_password_hash($post['password']);
		}
		$post['update_time'] = time();
		
		$id = isset($post['id']) ? $post['id'] : null;
		unset($post['id']);
		
		// 2. 过滤数据
		$fields = $this->db->list_fields($this->table_mapper['master']);
		$master_data = $this->get_allowed_write_data($post, null, $fields);
		
		$fields = $this->db->list_fields($this->table_mapper['info']);
		$info_data = $this->get_allowed_write_data($post, null, $fields);
		
		// 3. 执行插入
		$boolean = false;
		// 管理员可否更改这些不可变信息？
		unset($master_data['name']);
		unset($master_data['email']);
		unset($master_data['mobile']);
		if (!empty($master_data)) {
			$boolean = $this->db->update($this->table_mapper['master'], $master_data, "id=$id");
		}
		
		// 暂不更新偏好设置这种格式不定的串行化信息
		unset($master_data['preferences']);
		// 如果只有一个更新时间字段待更新，那不更新该字段。
		// 不过，即使这里确实更新了 update_time 字段，也并不能保证其它信息一定有改动过(affected_rows>0)！比如说修改了，但修改后的结果和之前的完全一样，除了更新时间。
		if (count($master_data) == 1) {
			unset($master_data['update_time']);
		}
		if (!empty($info_data)) {
			$boolean = $this->db->update($this->table_mapper['info'], $info_data, "id=$id");
		}
		
		// 4. 整理返回
		// MARK $boolean 为真并不意味着 $this->db->affected_rows() 大于 0！- affected_rows：如果提交修改的数据和原数据完全相同，则为 0！！！
		#$status = $this->db->affected_rows() > 0;
		$status = $boolean;
		$content = array_merge($master_data, $info_data);
		
		return [$status, $content];
	}
	
	public function verify_member_password($password_text, $password_hash)
	{
		return self::calculate_password_verify($password_text, $password_hash);
	}
	
	public function update_member_password($member_id, $password)
	{
		$password = self::calculate_password_hash($password);
		$this->db->where('id', $member_id);
		$this->db->update($this->table_mapper['master'], ['password'=>$password]);
		$affected_rows = $this->db->affected_rows();
		
		return $affected_rows > 0;
	}
	
	public function get_member_preferences($member_id)
	{
		$member = $this->db
						->select('id, preferences')
						->WHERE('id', $member_id)
						->get($this->table_mapper['info'])
						->row_array();
		$p = @unserialize($member['preferences']) OR $p = [];
		
		return $p;
	}
	
	public function set_member_preferences($member_id, $value, $key = null)
	{
		$preferences = $value;
		if (!is_null($key)) {
			$preferences = $this->get_member_preferences($member_id);
			$preferences[$key] = $value;
		}
		
		return $this->db->update($this->table_mapper['info'], 
				['preferences' => serialize($preferences)], "id=$member_id");
	}
	
	
	private static function calculate_password_hash($password_plain_text)
	{
		return password_hash($password_plain_text, PASSWORD_DEFAULT);
	}
	
	private static function calculate_password_verify($password_text, $password_hash)
	{
		return password_verify($password_text, $password_hash);
	}
}